<template>
  <RelationGraph class="graph" ref="graphRef" :options="graphOptions" v-loading="graphLoading">
    <!-- 节点插槽 -->
    <template #node="{node}">
      <!-- 默认插槽实现，也可以在父组件中按需实现 -->
      <slot name="node" :node="node">
        <div class="node-container">
          <!-- 动画节点 -->
          <div v-if="showNodeAnimation && selectedGraphLayout !== '树状布局'" style="width: 100%; height: 100%;">
            <!-- 节点背景动画 -->
            <div class="node-background-animation" :style="{'background-image': `url(${getNodeBackground(node)})`, 'background-size': '100%'}">
            </div>
            <!-- 节点主体 -->
            <div class="node-body-animation" :style="{'background-image': `url(${getNodeIcon(node, showIcon)})`, 'background-size': '100%'}">
            </div>
          </div>
          <!-- 默认节点 -->
          <div v-else-if="selectedGraphLayout !== '树状布局'" class="node-default" :style="{'background-image': `url(${getNodeIcon(node, showIcon)})`}"></div>
          <!-- 节点名称 -->
          <div class="node-name" :style="{color:node.color}">{{node.text}}</div>
        </div>
      </slot>
    </template>
    <!-- 图谱插槽 -->
    <template #graph-plug>
      <!-- 默认插槽实现，也可以在父组件中按需实现 -->
      <slot name="graph-plug">
        <!-- 上方工具栏 -->
        <div class="toolbar toolbar-top">
          <div class="tool-item">
            <div class="tool-item">
              <div class="item-title">查询条件：</div>
              <el-input v-model="queryParams.input" placeholder="请输入查询条件" style="width: 240px;">
                <el-button slot="append" icon="el-icon-search" @click="handleSearch"></el-button>
              </el-input>
            </div>
            &nbsp;&nbsp;&nbsp;&nbsp;
            <div class="tool-item">
              <div class="item-title">布局方式：</div>
              <el-radio-group v-model="selectedGraphLayout" size="mini" @change="handleGraphLayoutChange">
                <el-radio-button v-for="(item, index) in graphLayouts" :key="`graph-layout-${index}`" :label="item"></el-radio-button>
              </el-radio-group>
              <el-radio-group v-if="selectedGraphLayout === '树状布局'" v-model="selectedTreeLayout" size="mini" @change="handleTreeLayoutChange" style="margin-left: 10px;">
                <el-radio-button v-for="(item, index) in treeLayouts" :key="`tree-layout-${index}`" :label="item"></el-radio-button>
              </el-radio-group>
            </div>
          </div>
        </div>
        <!-- 下方工具栏 -->
        <div class="toolbar toolbar-bottom">          
          <div class="tool-item">
            <div class="tool-item">
              <div class="item-title">显示头像：</div>
              <el-switch v-model="showIcon" :disabled="selectedGraphLayout === '树状布局'"></el-switch>
            </div>
            &nbsp;&nbsp;&nbsp;&nbsp;
            <div class="tool-item">
              <div class="item-title">显示动画：</div>
              <el-switch v-model="showNodeAnimation" :disabled="selectedGraphLayout === '树状布局'"></el-switch>
            </div>
          </div>
          <!-- 节点筛选 -->
          <div class="tool-item" style="margin-top: 10px;">
            <div class="tool-item" v-for="(item, index) in filterOptions" :key="index">
              <div class="item-title">{{ `${item.title}：` }}</div>
              <el-radio-group v-model="item.value" size="mini" @change="handleFilterDataType">
                <el-radio-button v-for="(subItem, subIndex) in item.options" :key="`sub-${subIndex}`" :label="subItem.value">{{ subItem.label }}</el-radio-button>
              </el-radio-group>
              &nbsp;&nbsp;&nbsp;&nbsp;
            </div>
          </div>
          <!-- 关系筛选 -->
          <div class="tool-item" style="margin-top: 10px;">
            <div class="item-title">关系筛选：</div>
            <el-checkbox style="margin-right: 30px;" :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">全选</el-checkbox>
            <el-checkbox-group v-model="selectedLineTypes" @change="handleFilterLineType">
              <el-checkbox v-for="thisItem in lineTypes" :key="thisItem" :label="thisItem" />
            </el-checkbox-group>
          </div>
        </div>
      </slot>
    </template>
  </RelationGraph>
</template>

<script>
// 如果您没有在main.js文件中使用Vue.use(RelationGraph); 就需要使用下面这一行代码来引入relation-graph
import RelationGraph from 'relation-graph';
import demoData from './graphData.json';

import { getGraphOptions, layoutForce, layoutCenter, layoutTreeH, layoutTreeV, graphLayouts, treeLayouts, getNodeIcon, getNodeBackground } from './graph';

export default {
  name: 'RelationGraphDemo',
  components: { RelationGraph }, // 如果您没有在main.js文件中使用Vue.use(RelationGraph); 就需要在这里注册：components: { RelationGraph }
  props: {
    dataSource: {
      type: Object,
      default: null,
    }
  },
  watch: {
    dataSource: {
      handler(newVal, _) {
        this.handleDataSource(newVal)
      },
      deep: true,
      immediate: true
    }
  },
  data() {
    return {
      graphLoading: false,
      graphJsonData: null,
      graphLayouts: ['力学布局', '中心布局', '树状布局'],
      selectedGraphLayout: '力学布局',
      treeLayouts: ['横向树状图谱', '纵向树状图谱'],
      selectedTreeLayout: '横向树状图谱',

      graphOptions: getGraphOptions(),

      queryParams: {
        input: ""
      },
      showNodeAnimation: true,
      showIcon: true,
      filterOptions: [
        { title: "性别筛选", key: "sexType", value: "", options: [{ label: "全部", value: ""}, {label: "男", value: "男"}, {label: "女", value: "女"}]},
        { title: "人物筛选", key: "isGoodMan", value: "", options: [{ label: "全部", value: ""}, {label: "正面人物", value: true}, {label: "反面人物", value: false}]}
      ],
      lineTypes: [],
      selectedLineTypes: [],
      isIndeterminate: false,
      checkAll: false,
    };
  },
  created() {
  },
  mounted() {
    this.handleGraphLayoutChange(this.selectedGraphLayout);
  },
  methods: {
    handleDataSource(obj) {
      if (!obj) return;

      this.graphJsonData = JSON.parse(JSON.stringify(obj));
      this.setLineTypes();
    },
    handleSearch() {
      this.setGraphData();
    },
    handleGraphLayoutChange(val) {
      switch (val) {
        case "力学布局":
          this.graphOptions = getGraphOptions(layoutForce);
          this.setGraphOptions();
          break;
        case "中心布局":
          this.graphOptions = getGraphOptions(layoutCenter);
          this.setGraphOptions();
          break;
        case "树状布局":
          this.handleTreeLayoutChange(this.selectedTreeLayout);
          break;
        default:
          break;
      }
    },
    handleTreeLayoutChange(val) {
      switch (val) {
        case "横向树状图谱":
          this.graphOptions = getGraphOptions(layoutTreeH);
          this.setGraphOptions();
          break;
        case "纵向树状图谱":
          this.graphOptions = getGraphOptions(layoutTreeV);
          this.setGraphOptions();
          break;
        default:
          break;
      }
    },
    showLoading(second) {
      const loading = this.$loading({
        lock: true,
        text: 'Loading',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      });
      setTimeout(() => {
        loading.close();
      }, second * 1000)
    },
    setGraphOptions() {
      this.showLoading(0.5);
      if (this.$refs.graphRef.getInstance()) {
        this.$refs.graphRef.getInstance().stopAutoLayout();
      }
      this.$refs.graphRef.setOptions(this.graphOptions, (graphInstance) => {
        graphInstance.setZoom(this.getFitZoom());
      });
    },
    getFitZoom() {
      var zoom = Math.round((window.innerHeight - 68 - 120) / 100) / 1.5 * 10; // (屏幕高 - 上下工具栏和margin高度) / 节点高度 / 1.5
      return zoom;
    },
    // 设置图谱数据
    setGraphData() {
      if (!this.graphJsonData) {
        this.graphJsonData = JSON.parse(JSON.stringify(demoData));
        this.setLineTypes();
      }
      this.graphJsonData.nodes = this.graphJsonData.nodes.map((item) => {
        item.color = "";
        item.borderColor = "";
        // item.expandHolderPosition = "right";
        // item.expanded = true;
        return item;
      });
      this.$refs.graphRef.setJsonData(this.graphJsonData, (graphInstance) => {
        // 这些写上当图谱初始化完成后需要执行的代码
        this.graphLoading = false;
      });
    },
    getNodeIcon,
    getNodeBackground,
    // 设置关系相关属性
    setLineTypes() {
      this.lineTypes = this.getAllLineTypes();
      this.selectedLineTypes = this.getAllLineTypes();
      this.isIndeterminate = true;
      this.checkAll = true;
    },
    // 获取数据源中全部关系
    getAllLineTypes() {
      return [...new Set(this.graphJsonData.lines.map(item => item.text))];
    },
    // 处理数据筛选
    handleFilterDataType(val) {
      // 当前 graph 对象的全部节点
      const _all_nodes = this.$refs.graphRef.getInstance().getNodes();
      _all_nodes.forEach(thisNode => {
        let _isHideThisLine = false;
        this.filterOptions.forEach(item => {
          if (item.value !== "") {
            if (thisNode.data[item.key] !== item.value) {
              _isHideThisLine = true;
            }
          }
        })
        thisNode.opacity = _isHideThisLine ? 0.1 : 1;
      });
      // 数据更新
      this.$refs.graphRef.getInstance().dataUpdated();
    },
    // 处理关系筛选
    handleFilterLineType(val) {
      // 处理全选相关的属性
      let checkedCount = val.length;
      this.checkAll = checkedCount === this.lineTypes.length;
      this.isIndeterminate = checkedCount > 0 && checkedCount < this.lineTypes.length;
    
      // 当前 graph 对象的全部关系
      const _all_links = this.$refs.graphRef.getInstance().getLinks();
      _all_links.forEach(thisLink => {
        thisLink.relations.forEach(thisLine => {
          if (this.selectedLineTypes.indexOf(thisLine.data['type']) === -1) {
            if (!thisLine.isHide) {
              thisLine.isHide = true;
              console.log('Hide line:', thisLine);
            }
          } else {
            if (thisLine.isHide) {
              thisLine.isHide = false;
              console.log('Show line:', thisLine);
            }
          }
        });
      });
      // 数据更新
      this.$refs.graphRef.getInstance().dataUpdated();
    },
    // 处理全选
    handleCheckAllChange(val) {
      this.selectedLineTypes = val ? this.lineTypes : [];
      this.isIndeterminate = false;
      this.handleFilterLineType(this.selectedLineTypes);
    }
  }
};
</script>

<style lang="scss" scoped>
.graph {
  width: 100%;
  height: 100%;
  .node-container {
    position: relative; 
    width: 100%;
    height: 100%;
    border: none !important;
    .node-background-animation {
      position: relative;
      display: flex;
      justify-content: center;
      align-items: center;
      background-repeat: no-repeat;
      background-position: center center;
      background-size: 100%;
      height:100%;
      width:100%;
      border-radius: 40px;
      overflow: visible;
      -webkit-animation: rotateAnimation 5s linear infinite;
              animation: rotateAnimation 5s linear infinite;
    }
    @keyframes rotateAnimation {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(360deg);
      }
    }
    .node-body-animation {
      position: absolute;
      top: 20%;
      left: 20%;
      width: 60%;
      height: 60%;
      border-radius: 50%;
    }
    .node-default {
      background-position: center center;
      background-size: 100%;
      height:100%;
      width:100%;
      border-radius: 40px;
      overflow: visible;
    }
    .node-name {
      width:80px;
      text-align:center;
      color: #2E74B5;
      margin-top: 10px;
    }
  }
  .toolbar {
    position: absolute;
    z-index:700;
    left: 15px;
    padding: 15px; 
    width: calc(100% - 30px);
    border: #efefef solid 1px;
    color: #555555;
    border-radius: 10px;
    background-color: rgba(255,255,255,0.79);
    font-size: 12px;
    .tool-item {
      display: flex;
      flex-direction: row;
      justify-content: flex-start;
      align-items: center;
      .item-title {
        width: 60px;
        line-height: 20px;
      }
    }
  }
  .toolbar-top {
    top: 15px;
  }
  .toolbar-bottom {
    bottom: 15px;
  }
}
</style>
